/********************************************************************************/
/*   The  Software  is  proprietary,  confidential,  and  valuable to Realtek   */
/*   Semiconductor  Corporation  ("Realtek").  All  rights, including but not   */
/*   limited  to  copyrights,  patents,  trademarks, trade secrets, mask work   */
/*   rights, and other similar rights and interests, are reserved to Realtek.   */
/*   Without  prior  written  consent  from  Realtek,  copying, reproduction,   */
/*   modification,  distribution,  or  otherwise  is strictly prohibited. The   */
/*   Software  shall  be  kept  strictly  in  confidence,  and  shall  not be   */
/*   disclosed to or otherwise accessed by any third party.                     */
/*   c<2003> - <2012>                                                           */
/*   The Software is provided "AS IS" without any warranty of any kind,         */
/*   express, implied, statutory or otherwise.                                  */
/********************************************************************************/

//----------------------------------------------------------------------------------------------------
// ID Code      : UserCommonLocalDimming.c No.0000
// Update Note  :
//----------------------------------------------------------------------------------------------------

#define __USER_COMMON_LOCAL_DIMMING__

#include "UserCommonInclude.h"

#if(_LOCAL_DIMMING_SUPPORT == _ON)

#pragma OT(8)

//****************************************************************************
// DEFINITIONS / MACROS
//****************************************************************************


//****************************************************************************
// STRUCT / TYPE / ENUM DEFINITTIONS
//****************************************************************************
StructLocalDimmingInfo g_stLocalDimmingInfo;


//****************************************************************************
// CODE TABLES
//****************************************************************************
WORD code tHDRToneMappingLv540[256] =
{
#ifdef _LD_HDR_TONE_MAPPING_LV540
#include _LD_HDR_TONE_MAPPING_LV540
#else
    0
#endif
};

WORD code tHDRToneMappingLv1200[256] =
{
#ifdef _LD_HDR_TONE_MAPPING_LV1200
#include _LD_HDR_TONE_MAPPING_LV1200
#else
    0
#endif
};

WORD code tSDRGamma18Lv[256] =
{
#ifdef _LD_SDR_GAMMA_18_LV
#include _LD_SDR_GAMMA_18_LV
#else
    0
#endif
};

WORD code tSDRGamma20Lv[256] =
{
#ifdef _LD_SDR_GAMMA_20_LV
#include _LD_SDR_GAMMA_20_LV
#else
    0
#endif
};

WORD code tSDRGamma22Lv[256] =
{
#ifdef _LD_SDR_GAMMA_22_LV
#include _LD_SDR_GAMMA_22_LV
#else
    0
#endif
};

WORD code tSDRGamma24Lv[256] =
{
#ifdef _LD_SDR_GAMMA_24_LV
#include _LD_SDR_GAMMA_24_LV
#else
    0
#endif
};

WORD code tSDRGamma26Lv[256] =
{
#ifdef _LD_SDR_GAMMA_26_LV
#include _LD_SDR_GAMMA_26_LV
#else
    0
#endif
};

WORD code tSDRGamma28Lv[256] =
{
#ifdef _LD_SDR_GAMMA_28_LV
#include _LD_SDR_GAMMA_28_LV
#else
    0
#endif
};

WORD code tSDRGamma30Lv[256] =
{
#ifdef _LD_SDR_GAMMA_30_LV
#include _LD_SDR_GAMMA_30_LV
#else
    0
#endif
};

WORD code tSDRPCMSRGBLv[256] =
{
#ifdef _LD_SDR_PCM_SRGB_LV
#include _LD_SDR_PCM_SRGB_LV
#else
    0
#endif
};

BYTE code tNodeLvMerged[((_PANEL_UNIFORMITY_LD_HOR_GRIDS * _PANEL_UNIFORMITY_LD_HOR_GRIDS) * 9)] =
{
#ifdef _LD_NODE_LV_MERGED
#include _LD_NODE_LV_MERGED
#else
    0
#endif
};

BYTE code tBaseLv[8] =
{
#ifdef _LD_BASE_LV
#include _LD_BASE_LV
#else
    0
#endif
};

code WORD *tSDRLV_TABLE[] =
{
    tSDRGamma18Lv,
    tSDRGamma20Lv,
    tSDRGamma22Lv,
    tSDRGamma24Lv,
    tSDRGamma26Lv,
    tSDRGamma28Lv,
    tSDRGamma30Lv,
    tSDRPCMSRGBLv,
};

BYTE code tLocalDimmingUniformityLvMergeTable[(_PANEL_LV_MAX - _PANEL_LV_MIN + 1) * 5] =
{
#ifdef _LD_UNIFORMITY_LV_MERGED
#include _LD_UNIFORMITY_LV_MERGED
#else
    0
#endif
};


//****************************************************************************
// VARIABLE DECLARATIONS
//****************************************************************************
BYTE g_pucUnifotmityGainTable[_GROUP_NODE_NUMBER * 5];
WORD g_pusUniformityGainNode[_GROUP_NODE_NUMBER];

WORD g_pusDCRToLV[_TOTAL_REGION];
WORD g_pusLastDCRToLV[_TOTAL_REGION];
WORD g_pusApplyPWM[_TOTAL_REGION];

WORD g_pusLastDeltaPWM[_TOTAL_REGION] =
{
    _PANEL_PWM_DELTA_MAX, _PANEL_PWM_DELTA_MAX, _PANEL_PWM_DELTA_MAX, _PANEL_PWM_DELTA_MAX,
    _PANEL_PWM_DELTA_MAX, _PANEL_PWM_DELTA_MAX, _PANEL_PWM_DELTA_MAX, _PANEL_PWM_DELTA_MAX,
};


//****************************************************************************
// FUNCTION DECLARATIONS
//****************************************************************************
void UserCommonLocalDimmingHandler(void);
EnumLDStatus UserCommonLocalDimmingGetEnableStutas(void);
void UserCommonLocalDimmingAdjust(void);
void UserCommonLocalDimmingDCRToLv(BYTE *pucDCRData8bit, WORD *pusDCRToLV);
void UserCommonLocalDimmingSetPWM1x8(WORD *pusDCRToLV, WORD *pusApplyPWM);
void UserCommonLocalDimmingPWMStepChange(WORD *pusDCRLvPwm, WORD *pusLastDeltaPWM, WORD *pusDCRToLV);
void UserCommonLocalDimmingPWMSmoothLevel(WORD *pusPWM, WORD usSmoothLevel);
void UserCommonLocalDimmingUniformityNodetoGainTable(WORD *pusUniformityGainNode, BYTE *pucUnifotmityGainTable, BYTE ucNodeClass);
void UserCommonLocalDimmingQuickSortDec(WORD xdata *pusArray, BYTE xdata *pucIndex);
void UserCommonLocalDimmingInitialSetting(bit bEn);
void UserCommonLocalDimmingSetLastPWMFull(void);
void UserCommonLocalDimmingStepToGDHDR(void);
void UserCommonLocalDimmingGetNodeLV1X8(WORD *pusUniformityGainNode, WORD *pusPWM, BYTE ucNodeClass);
void UserCommonLocalDimmingEnable(bit bEn);
void UserCommonLocalDimmingSetPWMValue(WORD *pusPWM);
void UserCommonLocalDimmingDCRToLvNonePCHDR(BYTE *pucDCRData8bit, WORD *pusDCRToLV);
void UserCommonLocalDimmingDCRToLvPCHDR(BYTE *pucDCRData8bit, WORD *pusDCRToLV);

//****************************************************************************
// FUNCTION DEFINITIONS
//****************************************************************************
//--------------------------------------------------
// Description  :
// Input Value  :
// Output Value :
//--------------------------------------------------
void UserCommonLocalDimmingHandler(void)
{
    if(UserCommonLocalDimmingGetEnableStutas() == _LD_ENABLE)
    {
        if(UserInterfaceGetLocaldimmingActionMode() == _LD_ACTION_MODE_DIMMING)
        {
            UserCommonLocalDimmingAdjust();
            SET_LD_ACTION_MODE_STATUS(_LD_ACTION_MODE_DIMMING);
        }
        else
        {
            if(GET_LD_ACTION_MODE_STATUS() == _LD_ACTION_MODE_DIMMING)
            {
                UserCommonLocalDimmingStepToGDHDR();
                SET_LD_ACTION_MODE_STATUS(_LD_ACTION_MODE_NORNAL);
            }
        }
    }
}

//--------------------------------------------------
// Description  : Judge Local Dimming ON or OFF
// Input Value  : None
// Output Value : LD_ENABLE OR LD_DISABLE
//--------------------------------------------------
EnumLDStatus UserCommonLocalDimmingGetEnableStutas(void)
{
    if((GET_ON_LINE_CALIBRATION_STATUS() == _CALIBRATION_OFF) && (UserInterfaceGetLocalDimmingStatus() == _LD_ENABLE))
    {
#if(_HDR10_SUPPORT == _ON)
        if(UserCommonHDRGetHDR10Status() == _HDR10_ON)
        {
            return _LD_ENABLE;
        }
        else
#endif
        {
            if(UserInterfaceGetLocalDimmingNoSupportStatus() == _LD_DISABLE)
            {
                return _LD_DISABLE;
            }
            else
            {
                return _LD_ENABLE;
            }
        }
    }
    else
    {
        return _LD_DISABLE;
    }
    return _LD_DISABLE;
}
//--------------------------------------------------
// Description  :
// Input Value  :
// Output Value :
//--------------------------------------------------
void UserCommonLocalDimmingAdjust(void)
{
    // Check if DCR data and DB ready
    if(ScalerColorDDomainDBStatus() == _NOT_WAIT)
    {
        BYTE pucDCRResult[_TOTAL_REGION];
        BYTE ucNodeClass = 0;
        memset(pucDCRResult, 255UL, _TOTAL_REGION);

        ScalerColorDCRLocalDimmingReadResult(pucDCRResult);
        UserCommonLocalDimmingDCRToLv(pucDCRResult, g_pusDCRToLV);


#if(_PANEL_REGION_MODE == _LD_1X8_MODE)
        UserCommonLocalDimmingSetPWM1x8(g_pusDCRToLV, g_pusApplyPWM);
#endif
        UserCommonLocalDimmingSetPWMValue(g_pusApplyPWM);
        DebugMessageHDR("g_pusApplyPWM[0]", g_pusApplyPWM[0]);
        DebugMessageHDR("g_pusApplyPWM[1]", g_pusApplyPWM[1]);
        DebugMessageHDR("g_pusApplyPWM[2]", g_pusApplyPWM[2]);
        DebugMessageHDR("g_pusApplyPWM[3]", g_pusApplyPWM[3]);
        DebugMessageHDR("g_pusApplyPWM[4]", g_pusApplyPWM[4]);
        DebugMessageHDR("g_pusApplyPWM[5]", g_pusApplyPWM[5]);
        DebugMessageHDR("g_pusApplyPWM[6]", g_pusApplyPWM[6]);
        DebugMessageHDR("g_pusApplyPWM[7]", g_pusApplyPWM[7]);

        for(ucNodeClass = 0; ucNodeClass < _NODE_CLASS_NUMBER; ucNodeClass++)
        {
#if(_PANEL_REGION_MODE == _LD_1X8_MODE)
            UserCommonLocalDimmingGetNodeLV1X8(g_pusUniformityGainNode, g_pusApplyPWM, ucNodeClass);
#endif
            // get uniformity table
            UserCommonLocalDimmingUniformityNodetoGainTable(g_pusUniformityGainNode, g_pucUnifotmityGainTable, ucNodeClass);

            // Write Uniformity table
            if(ucNodeClass == _NODE_CLASS_NUMBER - 1)
            {
                ScalerColorPanelUniformityLDTargetLUTAdjust(g_pucUnifotmityGainTable, ucNodeClass * _GROUP_NODE_NUMBER, _REMAINDER_NODE_NUMBER * 5);
                ScalerColorPanelUniformityLDSramToggle(_DB_APPLY_NO_POLLING);
            }
            else
            {
                ScalerColorPanelUniformityLDTargetLUTAdjust(g_pucUnifotmityGainTable, ucNodeClass * _GROUP_NODE_NUMBER, _GROUP_NODE_NUMBER * 5);
            }
        }
    }
}

//--------------------------------------------------
// Description  :
// Input Value  :
// Output Value :
//--------------------------------------------------
void UserCommonLocalDimmingDCRToLv(BYTE *pucDCRData8bit, WORD *pusDCRToLV)
{
    if(UserInterfaceGetPCHDRMode() == _LD_PCHDR_MODE)
    {
        UserCommonLocalDimmingDCRToLvPCHDR(pucDCRData8bit, pusDCRToLV);
    }
    else
    {
        UserCommonLocalDimmingDCRToLvNonePCHDR(pucDCRData8bit, pusDCRToLV);
    }
}

//--------------------------------------------------
// Description  :
// Input Value  :
// Output Value :
//--------------------------------------------------
void UserCommonLocalDimmingSetPWM1x8(WORD *pusDCRToLV, WORD *pusApplyPWM)
{
    BYTE data ucI = 0;
    WORD data pusDeltaPWM[_TOTAL_REGION];
    WORD xdata pusTarget_LV[_TOTAL_REGION];
    BYTE xdata pucRegionInd[_TOTAL_REGION] = {0, 1, 2, 3, 4, 5, 6, 7};
    memcpy(pusTarget_LV, pusDCRToLV, _TOTAL_REGION * sizeof(pusDCRToLV[0]));
    if(UserInterfaceGetPCHDRMode() == _LD_NONE_PCHDR_MODE)
    {
        for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
        {
            pusDeltaPWM[ucI] = _PANEL_PWM_DELTA_MAX / 2;
        }
    }
    else
    {
        memset(pusDeltaPWM, 0, sizeof(pusDeltaPWM));
    }

    // sort
    UserCommonLocalDimmingQuickSortDec(pusTarget_LV, pucRegionInd);

    // set increase PWM
    for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
    {
        switch(pucRegionInd[ucI])
        {
            case 0:

                if(pusDCRToLV[0] > tBaseLv[0] + pusDeltaPWM[1] / _REGION1_PWM_TO_LV_REGION0)
                {
                    pusDeltaPWM[0] = (pusDCRToLV[0] - tBaseLv[0] - pusDeltaPWM[1] / _REGION1_PWM_TO_LV_REGION0) * _REGION0_PWM_TO_LV_OWN;
                    pusDeltaPWM[0] = (pusDeltaPWM[0] > _PANEL_PWM_DELTA_MAX) ? _PANEL_PWM_DELTA_MAX : pusDeltaPWM[0];
                }

                break;

            case 1:

                if(pusDCRToLV[1] > tBaseLv[1] + pusDeltaPWM[0] / _REGION0_PWM_TO_LV_REGION1 + pusDeltaPWM[2] / _REGION2_PWM_TO_LV_REGION1)
                {
                    pusDeltaPWM[1] = (pusDCRToLV[1] - tBaseLv[1] - pusDeltaPWM[0] / _REGION0_PWM_TO_LV_REGION1 - pusDeltaPWM[2] / _REGION2_PWM_TO_LV_REGION1) * _REGION1_PWM_TO_LV_OWN;
                    pusDeltaPWM[1] = (pusDeltaPWM[1] > _PANEL_PWM_DELTA_MAX) ? _PANEL_PWM_DELTA_MAX : pusDeltaPWM[1];
                }

                break;

            case 2:

                if(pusDCRToLV[2] > tBaseLv[2] + pusDeltaPWM[1] / _REGION1_PWM_TO_LV_REGION2 + pusDeltaPWM[3] / _REGION3_PWM_TO_LV_REGION2)
                {
                    pusDeltaPWM[2] = (pusDCRToLV[2] - tBaseLv[2] - pusDeltaPWM[1] / _REGION1_PWM_TO_LV_REGION2 - pusDeltaPWM[3] / _REGION3_PWM_TO_LV_REGION2) * _REGION2_PWM_TO_LV_OWN;
                    pusDeltaPWM[2] = (pusDeltaPWM[2] > _PANEL_PWM_DELTA_MAX) ? _PANEL_PWM_DELTA_MAX : pusDeltaPWM[2];
                }

                break;

            case 3:

                if(pusDCRToLV[3] > tBaseLv[3] + pusDeltaPWM[2] / _REGION2_PWM_TO_LV_REGION3 + pusDeltaPWM[4] / _REGION4_PWM_TO_LV_REGION3)
                {
                    pusDeltaPWM[3] = (pusDCRToLV[3] - tBaseLv[3] - pusDeltaPWM[2] / _REGION2_PWM_TO_LV_REGION3 - pusDeltaPWM[4] / _REGION4_PWM_TO_LV_REGION3) * _REGION3_PWM_TO_LV_OWN;
                    pusDeltaPWM[3] = (pusDeltaPWM[3] > _PANEL_PWM_DELTA_MAX) ? _PANEL_PWM_DELTA_MAX : pusDeltaPWM[3];
                }

                break;

            case 4:

                if(pusDCRToLV[4] > tBaseLv[4] + pusDeltaPWM[3] / _REGION3_PWM_TO_LV_REGION4 + pusDeltaPWM[5] / _REGION5_PWM_TO_LV_REGION4)
                {
                    pusDeltaPWM[4] = (pusDCRToLV[4] - tBaseLv[4] - pusDeltaPWM[3] / _REGION3_PWM_TO_LV_REGION4 - pusDeltaPWM[5] / _REGION5_PWM_TO_LV_REGION4) * _REGION4_PWM_TO_LV_OWN;
                    pusDeltaPWM[4] = (pusDeltaPWM[4] > _PANEL_PWM_DELTA_MAX) ? _PANEL_PWM_DELTA_MAX : pusDeltaPWM[4];
                }

                break;

            case 5:

                if(pusDCRToLV[5] > tBaseLv[5] + pusDeltaPWM[4] / _REGION4_PWM_TO_LV_REGION5 + pusDeltaPWM[6] / _REGION6_PWM_TO_LV_REGION5)
                {
                    pusDeltaPWM[5] = (pusDCRToLV[5] - tBaseLv[5] - pusDeltaPWM[4] / _REGION4_PWM_TO_LV_REGION5 - pusDeltaPWM[6] / _REGION6_PWM_TO_LV_REGION5) * _REGION5_PWM_TO_LV_OWN;
                    pusDeltaPWM[5] = (pusDeltaPWM[5] > _PANEL_PWM_DELTA_MAX) ? _PANEL_PWM_DELTA_MAX : pusDeltaPWM[5];
                }

                break;

            case 6:

                if(pusDCRToLV[6] > tBaseLv[6] + pusDeltaPWM[5] / _REGION5_PWM_TO_LV_REGION6 + pusDeltaPWM[7] / _REGION7_PWM_TO_LV_REGION6)
                {
                    pusDeltaPWM[6] = (pusDCRToLV[6] - tBaseLv[6] - pusDeltaPWM[5] / _REGION5_PWM_TO_LV_REGION6 - pusDeltaPWM[7] / _REGION7_PWM_TO_LV_REGION6) * _REGION6_PWM_TO_LV_OWN;
                    pusDeltaPWM[6] = (pusDeltaPWM[6] > _PANEL_PWM_DELTA_MAX) ? _PANEL_PWM_DELTA_MAX : pusDeltaPWM[6];
                }

                break;

            case 7:

                if(pusDCRToLV[7] > tBaseLv[7] + pusDeltaPWM[6] / _REGION6_PWM_TO_LV_REGION7)
                {
                    pusDeltaPWM[7] = (pusDCRToLV[7] - tBaseLv[7] - pusDeltaPWM[6] / _REGION6_PWM_TO_LV_REGION7) * _REGION7_PWM_TO_LV_OWN;
                    pusDeltaPWM[7] = (pusDeltaPWM[7] > _PANEL_PWM_DELTA_MAX) ? _PANEL_PWM_DELTA_MAX : pusDeltaPWM[7];
                }

                break;

            default:

                break;
        }
    }
    // smooth PWM process
    UserCommonLocalDimmingPWMSmoothLevel(pusDeltaPWM, UserInterfaceGetLocalDimmingPWMSmoothLevel());

    // step PWM
    UserCommonLocalDimmingPWMStepChange(pusDeltaPWM, g_pusLastDeltaPWM, pusDCRToLV);
    // Apply PWM
    memcpy(pusApplyPWM, pusDeltaPWM, 8 * sizeof(pusDeltaPWM[0]));

    for(ucI = 0; ucI < 8; ucI++)
    {
        pusApplyPWM[ucI] = pusDeltaPWM[ucI] + _PANEL_PWM_MIN;
    }
}
//--------------------------------------------------
// Description  :
// Input Value  :
// Output Value :
//--------------------------------------------------
void UserCommonLocalDimmingPWMStepChange(WORD *pusDCRLvPwm, WORD *pusLastDeltaPWM, WORD *pusDCRToLV)
{
    BYTE data ucI = 0;
    BYTE ucPWMStep = 0;
    EnumPCHDRMode enumPCHDRMode = UserInterfaceGetPCHDRMode();

    for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
    {
        if(pusDCRLvPwm[ucI] > pusLastDeltaPWM[ucI])
        {
            if((enumPCHDRMode == _LD_PCHDR_MODE) &&
               (pusDCRToLV[ucI] == (_PANEL_LV_MAX + g_pusLastDCRToLV[ucI])))
            {
                pusDCRLvPwm[ucI] = _PANEL_PWM_DELTA_MAX;
            }
            else
            {
                ucPWMStep = (pusDCRLvPwm[ucI] - pusLastDeltaPWM[ucI]) / _FRAME_NUMBER_TO_TARGET;
                ucPWMStep = (ucPWMStep == 0) ? 1 : ucPWMStep;
                if(pusDCRLvPwm[ucI] > (ucPWMStep + pusLastDeltaPWM[ucI]))
                {
                    pusDCRLvPwm[ucI] = pusLastDeltaPWM[ucI] + ucPWMStep;
                }
            }
        }
        else if(pusDCRLvPwm[ucI] < pusLastDeltaPWM[ucI])
        {
            ucPWMStep = (pusLastDeltaPWM[ucI] - pusDCRLvPwm[ucI]) / _FRAME_NUMBER_TO_TARGET;
            ucPWMStep = (ucPWMStep == 0) ? 1 : ucPWMStep;
            if(pusDCRLvPwm[ucI] + ucPWMStep < pusLastDeltaPWM[ucI])
            {
                pusDCRLvPwm[ucI] = pusLastDeltaPWM[ucI] - ucPWMStep;
            }
        }
    }
    memcpy(g_pusLastDCRToLV, pusDCRToLV, 8 * sizeof(pusDCRToLV[0]));
    memcpy(pusLastDeltaPWM, pusDCRLvPwm, 8 * sizeof(pusDCRLvPwm[0]));
}
//--------------------------------------------------
// Description  :
// Input Value  :
// Output Value :
//--------------------------------------------------
void UserCommonLocalDimmingPWMSmoothLevel(WORD *pusPWM, WORD usSmoothLevel)
{
    if(UserInterfaceGetPCHDRMode() == _LD_NONE_PCHDR_MODE)
    {
        BYTE xdata ucI = 0;

        usSmoothLevel = (_PANEL_PWM_SMOOTH_MIN - ((_PANEL_PWM_SMOOTH_MIN - _PANEL_PWM_SMOOTH_MAX) / _PANEL_PWM_SMOOTH_SECTION) * usSmoothLevel);

        for(ucI = 0; ucI < 7; ucI++)
        {
            if(pusPWM[ucI] > (pusPWM[ucI + 1] + usSmoothLevel))
            {
                pusPWM[ucI + 1] = pusPWM[ucI] - usSmoothLevel;
            }
            if(pusPWM[ucI + 1] > (pusPWM[ucI] + usSmoothLevel))
            {
                pusPWM[ucI] = pusPWM[ucI + 1] - usSmoothLevel;
            }
        }

        for(ucI = 6; ucI > 0; ucI--)
        {
            if(pusPWM[ucI] > (pusPWM[ucI - 1] + usSmoothLevel))
            {
                pusPWM[ucI - 1] = pusPWM[ucI] - usSmoothLevel;
            }
            if(pusPWM[ucI - 1] > (pusPWM[ucI] + usSmoothLevel))
            {
                pusPWM[ucI] = pusPWM[ucI - 1] - usSmoothLevel;
            }
        }
    }
}
//--------------------------------------------------
// Description  :
// Input Value  :
// Output Value :
//--------------------------------------------------
void UserCommonLocalDimmingUniformityNodetoGainTable(WORD *pusUniformityGainNode, BYTE *pucUnifotmityGainTable, BYTE ucNodeClass)
{
    BYTE data ucI = 0;
    BYTE data ucJ = 0;
    WORD *pusUniformityGainNodeOffset;
    data WORD usUniformityGainTablePosition = 0;
    BYTE data ucMaxIdx = 0;
    BYTE data ucQuot = 0;
    BYTE data ucRemind = 0;

    if(ucNodeClass < (_NODE_CLASS_NUMBER - 1))
    {
        ucQuot = _GROUP_NODE_NUMBER / 255;
        ucRemind = _GROUP_NODE_NUMBER % 255;
    }
    else if(ucNodeClass == (_NODE_CLASS_NUMBER - 1))
    {
        ucQuot = _REMAINDER_NODE_NUMBER / 255;
        ucRemind = _REMAINDER_NODE_NUMBER % 255;
    }

    for(ucI = 0; ucI <= ucQuot; ucI++)
    {
        pusUniformityGainNodeOffset = &pusUniformityGainNode[(WORD)(ucI)*0xFF];

        if(ucI != ucQuot)
        {
            ucMaxIdx=0xFF;
        }
        else
        {
            ucMaxIdx = ucRemind;
        }

        for(ucJ = 0; ucJ < ucMaxIdx; ucJ++)
        {
            data WORD usUniformityGainNode_ucI = pusUniformityGainNodeOffset[ucJ];

            if((usUniformityGainNode_ucI > _PANEL_LV_MAX))
            {
                usUniformityGainTablePosition = (_PANEL_LV_MAX - _PANEL_LV_MIN);
            }
            else if(usUniformityGainNode_ucI < _PANEL_LV_MIN)
            {
                usUniformityGainTablePosition = (_PANEL_LV_MIN - _PANEL_LV_MIN);
            }
            else
            {
                usUniformityGainTablePosition = usUniformityGainNode_ucI - _PANEL_LV_MIN;
            }

            memcpy(pucUnifotmityGainTable, tLocalDimmingUniformityLvMergeTable + ((WORD)usUniformityGainTablePosition) * 5, 5);
            pucUnifotmityGainTable += 5;
        }
    }
}

//--------------------------------------------------
// Description  :
// Input Value  :
// Output Value :
//--------------------------------------------------
void UserCommonLocalDimmingQuickSortDec(WORD xdata *pusArray, BYTE xdata *pucIndex)
{
    BYTE data ucSwap = 0;
    BYTE data ucTempIndex = 0;
    WORD usPivot = 0;
    SBYTE data chI = 0;
    SBYTE data chLeft = 0;
    SBYTE data chRight = 0;
    BYTE data pucBegin[6];
    BYTE data pucEnd[6];
    ((DWORD *)&(pucIndex[0]))[0] = 0x00010203;
    ((DWORD *)&(pucIndex[4]))[0] = 0x04050607;

    pucBegin[0] = 0;
    pucEnd[0] = 8;

    while(chI >= 0)
    {
        chLeft = pucBegin[chI];
        chRight = pucEnd[chI] - 1;

        if(chLeft < chRight)
        {
            usPivot = pusArray[chLeft];
            ucTempIndex = pucIndex[chLeft];

            while(chLeft < chRight)
            {
                while((pusArray[chRight] <= usPivot) && (chLeft < chRight))
                {
                    chRight--;
                }

                if(chLeft < chRight)
                {
                    pucIndex[chLeft] = pucIndex[chRight];
                    pusArray[chLeft++] = pusArray[chRight];
                }

                while((pusArray[chLeft] >= usPivot) && (chLeft < chRight))
                {
                    chLeft++;
                }

                if(chLeft < chRight)
                {
                    pucIndex[chRight] = pucIndex[chLeft];
                    pusArray[chRight--] = pusArray[chLeft];
                }
            }

            pusArray[chLeft] = usPivot;
            pucIndex[chLeft] = ucTempIndex;
            pucBegin[chI + 1] = chLeft + 1;
            pucEnd[chI + 1] = pucEnd[chI];
            pucEnd[chI++] = chLeft;

            if(pucEnd[chI] - pucBegin[chI] > pucEnd[chI - 1] - pucBegin[chI - 1])
            {
                ucSwap = pucBegin[chI];
                pucBegin[chI] = pucBegin[chI - 1];
                pucBegin[chI - 1] = ucSwap;

                ucSwap = pucEnd[chI];
                pucEnd[chI] = pucEnd[chI - 1];
                pucEnd[chI - 1] = ucSwap;
            }
        }
        else
        {
            chI--;
        }
    }
}
//--------------------------------------------------
// Description  :
// Input Value  :
// Output Value :
//--------------------------------------------------
void UserCommonLocalDimmingInitialSetting(bit bEn)
{
    if(bEn == _ENABLE)
    {
        ScalerColorPanelUniformityEnable(_FUNCTION_OFF);
        ScalerTimerWaitForEvent(_EVENT_DVS);
        ScalerTimerDelayXms(2);

        // DCR
        ScalerColorDCRLocalDimmingEnable(_FUNCTION_ON);
        ScalerColorDCRLocalDimmingSetMode(_LOCAL_DIMMING_1X8_MODE);
        ScalerColorDCRLocalDimmingSetTCon(_LOCAL_DIMMING_1X8_MODE);

        // Uniformity
        ScalerColorPanelUniformityLDInitial(_UNIFORMITY_PCM_MODE);
        ScalerColorPanelUniformityLDEnable(_FUNCTION_ON, _DB_APPLY_POLLING);
        UserCommonLocalDimmingSetLastPWMFull();
        ScalerColorPanelUniformityEnable(_FUNCTION_ON);

#if(_HW_GLOBAL_DB_TYPE == _GLOBAL_DB_GEN_III)
        // Global DB
        ScalerColorDDomainDBEnable(_ENABLE);
        ScalerColorDDomainDBTriggerEvent(_DB_VS_TRIGGER);
#endif
    }
    else
    {
        // DCR
        ScalerColorDCRLocalDimmingEnable(_FUNCTION_OFF);

        // Uniformity
        ScalerColorPanelUniformityLDEnable(_FUNCTION_OFF, _DB_APPLY_POLLING);

#if(_UNIFORMITY_SUPPORT == _ON)
        ScalerColorPanelUniformityInitial(_PANEL_UNIFORMITY_MULTI_LUT_MODE);
        if(UserInterfaceGetUniformityStatus() == _PANEL_UNIFORMITY_OFF)
        {
            ScalerColorPanelUniformityEnable(_FUNCTION_OFF);
        }
#endif

#if(_HW_GLOBAL_DB_TYPE == _GLOBAL_DB_GEN_III)
        // Global DB
        ScalerColorDDomainDBEnable(_DISABLE);
#endif
    }

    SET_LD_ACTION_MODE_STATUS(_LD_ACTION_MODE_NORNAL);
}

//--------------------------------------------------
// Description  :
// Input Value  :
// Output Value :
//--------------------------------------------------
void UserCommonLocalDimmingSetLastPWMFull(void)
{
    BYTE ucI = 0;

    for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
    {
        g_pusLastDeltaPWM[ucI] = _PANEL_PWM_DELTA_MAX;
    }
}

//--------------------------------------------------
// Description  :
// Input Value  :
// Output Value :
//--------------------------------------------------
void UserCommonLocalDimmingStepToGDHDR(void)
{
    BYTE ucI = 0;
    BYTE ucFinishFlag = 0;
    BYTE ucTimeout = 30;
    BYTE pucRegionFlag[_TOTAL_REGION] = {0};
    BYTE ucNodeClass = 0;

    if(ScalerColorDDomainDBStatus() == _WAIT)
    {
        ScalerTimerWaitForEvent(_EVENT_DVS);
    }

    while(ucFinishFlag != _TOTAL_REGION)
    {
        ucFinishFlag = 0;
        for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
        {
            g_pusApplyPWM[ucI] = g_pusApplyPWM[ucI] + _PANEL_PWM_STEP_TO_GOLBAL;

            if(g_pusApplyPWM[ucI] >= _PANEL_PWM_MAX)
            {
                g_pusApplyPWM[ucI] = _PANEL_PWM_MAX;
                pucRegionFlag[ucI] = 1;
            }
            else
            {
                pucRegionFlag[ucI] = 0;
            }
        }

        UserCommonLocalDimmingSetPWMValue(g_pusApplyPWM);

        DebugMessageHDR("StepToGB[0]", g_pusApplyPWM[0]);
        DebugMessageHDR("StepToGB[1]", g_pusApplyPWM[1]);
        DebugMessageHDR("StepToGB[2]", g_pusApplyPWM[2]);
        DebugMessageHDR("StepToGB[3]", g_pusApplyPWM[3]);
        DebugMessageHDR("StepToGB[4]", g_pusApplyPWM[4]);
        DebugMessageHDR("StepToGB[5]", g_pusApplyPWM[5]);
        DebugMessageHDR("StepToGB[6]", g_pusApplyPWM[6]);
        DebugMessageHDR("StepToGB[7]", g_pusApplyPWM[7]);

        for(ucNodeClass = 0; ucNodeClass < _NODE_CLASS_NUMBER; ucNodeClass++)
        {
            UserCommonLocalDimmingGetNodeLV1X8(g_pusUniformityGainNode, g_pusApplyPWM, ucNodeClass);
            UserCommonLocalDimmingUniformityNodetoGainTable(g_pusUniformityGainNode, g_pucUnifotmityGainTable, ucNodeClass);
            if(ucNodeClass == _NODE_CLASS_NUMBER - 1)
            {
                ScalerColorPanelUniformityLDTargetLUTAdjust(g_pucUnifotmityGainTable, ucNodeClass * _GROUP_NODE_NUMBER, _REMAINDER_NODE_NUMBER * 5);
                ScalerColorPanelUniformityLDSramToggle(_DB_APPLY_POLLING);
            }
            else
            {
                ScalerColorPanelUniformityLDTargetLUTAdjust(g_pucUnifotmityGainTable, ucNodeClass * _GROUP_NODE_NUMBER, _GROUP_NODE_NUMBER * 5);
            }
        }

        for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
        {
            ucFinishFlag += pucRegionFlag[ucI];
        }

        // Check timeout counter
        if(ucTimeout-- == 0)
        {
            for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
            {
                g_pusApplyPWM[ucI] = _PANEL_PWM_MAX;
            }
            UserCommonLocalDimmingSetPWMValue(g_pusApplyPWM);

            for(ucNodeClass = 0; ucNodeClass < _NODE_CLASS_NUMBER; ucNodeClass++)
            {
                UserCommonLocalDimmingGetNodeLV1X8(g_pusUniformityGainNode, g_pusApplyPWM, ucNodeClass);
                UserCommonLocalDimmingUniformityNodetoGainTable(g_pusUniformityGainNode, g_pucUnifotmityGainTable, ucNodeClass);
                if(ucNodeClass == _NODE_CLASS_NUMBER - 1)
                {
                    ScalerColorPanelUniformityLDTargetLUTAdjust(g_pucUnifotmityGainTable, ucNodeClass * _GROUP_NODE_NUMBER, _REMAINDER_NODE_NUMBER * 5);
                    ScalerColorPanelUniformityLDSramToggle(_DB_APPLY_POLLING);
                }
                else
                {
                    ScalerColorPanelUniformityLDTargetLUTAdjust(g_pucUnifotmityGainTable, ucNodeClass * _GROUP_NODE_NUMBER, _GROUP_NODE_NUMBER * 5);
                }
            }
            break;
        }
    }

    UserCommonLocalDimmingSetLastPWMFull();
}

//--------------------------------------------------
// Description  :
// Input Value  :
// Output Value :
//--------------------------------------------------
void UserCommonLocalDimmingGetNodeLV1X8(WORD *pusUniformityGainNode, WORD *pusPWM, BYTE ucNodeClass)
{
    BYTE ucI = 0;
    BYTE ucJ = 0;
    data BYTE code *pucData = tNodeLvMerged;
    WORD data usNodeIndex = 0;
    WORD data pusLocalPwm[_TOTAL_REGION];
    BYTE data ucMaxIdx = 0;
    BYTE ucQuot = 0;
    BYTE ucRemind = 0;

    for(ucI = 0; ucI < (sizeof(pusLocalPwm) / sizeof(pusLocalPwm[0])); ++ucI)
    {
        pusLocalPwm[ucI] = pusPWM[ucI] - _PANEL_PWM_MIN;
        pusLocalPwm[ucI] *= 16;

        if(LOBYTE(pusLocalPwm[ucI]) >= 128)
        {
            pusLocalPwm[ucI] += 256;
        }
    }

    if(ucNodeClass < (_NODE_CLASS_NUMBER - 1))
    {
        ucQuot = _GROUP_NODE_NUMBER / 255;
        ucRemind = _GROUP_NODE_NUMBER % 255;
    }
    else if(ucNodeClass == (_NODE_CLASS_NUMBER - 1))
    {
        ucQuot = _REMAINDER_NODE_NUMBER / 255;
        ucRemind = _REMAINDER_NODE_NUMBER % 255;
    }

    pucData = pucData + ucNodeClass * _GROUP_NODE_NUMBER * 9;

    for(ucI = 0; ucI <= ucQuot; ucI++)
    {
        if(ucI != ucQuot)
        {
            ucMaxIdx=0xFF;
        }
        else
        {
            ucMaxIdx = ucRemind;
        }

        for(ucJ = 0; ucJ < ucMaxIdx; ucJ++)
        {
            pusUniformityGainNode[usNodeIndex] =
                ((BYTEPTR(pusLocalPwm)[0] * pucData[0] +
                  BYTEPTR(pusLocalPwm)[2] * pucData[1] +
                  BYTEPTR(pusLocalPwm)[4] * pucData[2] +
                  BYTEPTR(pusLocalPwm)[6] * pucData[3] +
                  BYTEPTR(pusLocalPwm)[8] * pucData[4] +
                  BYTEPTR(pusLocalPwm)[10] * pucData[5] +
                  BYTEPTR(pusLocalPwm)[12] * pucData[6] +
                  BYTEPTR(pusLocalPwm)[14] * pucData[7]) / 32 + pucData[8]);
            usNodeIndex++;
            pucData += 9;
        }
    }
}

//--------------------------------------------------
// Description  : Enable Local Dimming Function in OSD
// Input Value  : EnumSelRegion, _Enable or _Disable
// Output Value : void
//--------------------------------------------------
void UserCommonLocalDimmingEnable(bit bEn)
{
    ScalerTimerWaitForEvent(_EVENT_DEN_STOP);
    ScalerColorOutputGammaEnable(_FUNCTION_OFF);
    ScalerColorSRGBEnable(_FUNCTION_OFF);
    ScalerColorPCMInputGammaEnable(_FUNCTION_OFF);

    UserCommonLocalDimmingInitialSetting(bEn);

#if(_HDR10_SUPPORT == _ON)
    if(UserCommonHDRGetHDR10Status() == _HDR10_ON)
    {
        WORD usOCCGammaTableArray = ((bEn == _ON) ? _LD_OCC_GAMMA10_ADDRESS : _OCC_GAMMA10_ADDRESS);

        ScalerColorPCMOutputGammaAdjust(g_pucDISP_CALIBRATION_FLASH + usOCCGammaTableArray, _OGC_FLASH_BANK);

        ScalerTimerWaitForEvent(_EVENT_DEN_STOP);
        ScalerColorPanelUniformityEnable((EnumFunctionOnOFF)bEn);
        ScalerColorOutputGammaEnable(_FUNCTION_ON);
        ScalerColorSRGBEnable(_FUNCTION_ON);
        ScalerColorPCMInputGammaEnable(_FUNCTION_ON);
    }
    else
#endif
    {
        UserInterfaceLocalDimmingAdjustPCM();
    }
}

//--------------------------------------------------
// Description  : Set driver PWM
// Input Value  : Panel Apply PWM
// Output Value : void
//--------------------------------------------------
void UserCommonLocalDimmingSetPWMValue(WORD *pusPWM)
{
    BYTE ucI = 0;
    WORD pusNormPWM[_TOTAL_REGION];

    for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
    {
        pusNormPWM[ucI] = ((DWORD)pusPWM[ucI] * _DEVICE_LED_DRIVER_PWM_MAX) / _PANEL_PWM_MAX;
    }
    UserInterfaceLocalDimmingAdjustPwmArray(pusNormPWM);
}

//--------------------------------------------------
// Description  : DCR to Lv PCHDR mode
// Input Value  : DCR 1*8 regions data
// Output Value : void
//--------------------------------------------------
void UserCommonLocalDimmingDCRToLvNonePCHDR(BYTE *pucDCRData8bit, WORD *pusDCRToLV)
{
    BYTE ucI = 0;

#if(_HDR10_SUPPORT == _ON)
    if(UserCommonHDRGetHDR10Status() == _HDR10_ON)
    {
        for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
        {
            pusDCRToLV[ucI] = tHDRToneMappingLv1200[pucDCRData8bit[ucI]];

            if(pusDCRToLV[ucI] < _PANEL_LV_MIN)
            {
                pusDCRToLV[ucI] = _PANEL_LV_MIN;
            }
        }
    }
    else
#endif
    {
        switch(UserInterfaceGetLocalDimmingColorMode())
        {
            case _LD_PCM_SRGB:

                for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
                {
                    pusDCRToLV[ucI] = tSDRPCMSRGBLv[pucDCRData8bit[ucI]];

                    if(pusDCRToLV[ucI] < _PANEL_LV_MIN)
                    {
                        pusDCRToLV[ucI] = _PANEL_LV_MIN;
                    }
                }

                break;

            case _LD_GAMMA_18:

                for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
                {
                    pusDCRToLV[ucI] = tSDRGamma18Lv[pucDCRData8bit[ucI]];

                    if(pusDCRToLV[ucI] < _PANEL_LV_MIN)
                    {
                        pusDCRToLV[ucI] = _PANEL_LV_MIN;
                    }
                }

                break;

            case _LD_GAMMA_20:

                for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
                {
                    pusDCRToLV[ucI] = tSDRGamma20Lv[pucDCRData8bit[ucI]];

                    if(pusDCRToLV[ucI] < _PANEL_LV_MIN)
                    {
                        pusDCRToLV[ucI] = _PANEL_LV_MIN;
                    }
                }

                break;

            case _LD_GAMMA_22:
            case _LD_PCM_ADOBE:

                for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
                {
                    pusDCRToLV[ucI] = tSDRGamma22Lv[pucDCRData8bit[ucI]];

                    if(pusDCRToLV[ucI] < _PANEL_LV_MIN)
                    {
                        pusDCRToLV[ucI] = _PANEL_LV_MIN;
                    }
                }

                break;

            case _LD_GAMMA_24:

                for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
                {
                    pusDCRToLV[ucI] = tSDRGamma24Lv[pucDCRData8bit[ucI]];

                    if(pusDCRToLV[ucI] < _PANEL_LV_MIN)
                    {
                        pusDCRToLV[ucI] = _PANEL_LV_MIN;
                    }
                }

                break;

            case _LD_GAMMA_26:

                for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
                {
                    pusDCRToLV[ucI] = tSDRGamma26Lv[pucDCRData8bit[ucI]];

                    if(pusDCRToLV[ucI] < _PANEL_LV_MIN)
                    {
                        pusDCRToLV[ucI] = _PANEL_LV_MIN;
                    }
                }

                break;

            case _LD_GAMMA_28:

                for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
                {
                    pusDCRToLV[ucI] = tSDRGamma28Lv[pucDCRData8bit[ucI]];

                    if(pusDCRToLV[ucI] < _PANEL_LV_MIN)
                    {
                        pusDCRToLV[ucI] = _PANEL_LV_MIN;
                    }
                }

                break;

            case _LD_GAMMA_30:

                for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
                {
                    pusDCRToLV[ucI] = tSDRGamma30Lv[pucDCRData8bit[ucI]];

                    if(pusDCRToLV[ucI] < _PANEL_LV_MIN)
                    {
                        pusDCRToLV[ucI] = _PANEL_LV_MIN;
                    }
                }

                break;

            default:

                break;
        }
    }
}

//--------------------------------------------------
// Description  : DCR to Lv NONEPCHDR mode
// Input Value  : DCR 1*8 regions data
// Output Value : void
//--------------------------------------------------
void UserCommonLocalDimmingDCRToLvPCHDR(BYTE *pucDCRData8bit, WORD *pusDCRToLV)
{
    BYTE ucI = 0;

#if(_HDR10_SUPPORT == _ON)
    if(UserCommonHDRGetHDR10Status() == _HDR10_ON)
    {
        for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
        {
            pusDCRToLV[ucI] = tHDRToneMappingLv540[pucDCRData8bit[ucI]];
        }
    }
    else
#endif
    {
        switch(UserInterfaceGetLocalDimmingColorMode())
        {
            case _LD_PCM_SRGB:

                for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
                {
                    pusDCRToLV[ucI] = tSDRPCMSRGBLv[pucDCRData8bit[ucI]];
                }

                break;

            case _LD_GAMMA_18:

                for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
                {
                    pusDCRToLV[ucI] = tSDRGamma18Lv[pucDCRData8bit[ucI]];
                }

                break;

            case _LD_GAMMA_20:

                for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
                {
                    pusDCRToLV[ucI] = tSDRGamma20Lv[pucDCRData8bit[ucI]];
                }

                break;

            case _LD_GAMMA_22:
            case _LD_PCM_ADOBE:

                for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
                {
                    pusDCRToLV[ucI] = tSDRGamma22Lv[pucDCRData8bit[ucI]];
                }

                break;

            case _LD_GAMMA_24:

                for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
                {
                    pusDCRToLV[ucI] = tSDRGamma24Lv[pucDCRData8bit[ucI]];
                }

                break;

            case _LD_GAMMA_26:

                for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
                {
                    pusDCRToLV[ucI] = tSDRGamma26Lv[pucDCRData8bit[ucI]];
                }

                break;

            case _LD_GAMMA_28:

                for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
                {
                    pusDCRToLV[ucI] = tSDRGamma28Lv[pucDCRData8bit[ucI]];
                }

                break;

            case _LD_GAMMA_30:

                for(ucI = 0; ucI < _TOTAL_REGION; ucI++)
                {
                    pusDCRToLV[ucI] = tSDRGamma30Lv[pucDCRData8bit[ucI]];
                }

                break;

            default:

                break;
        }
    }
}
#endif // End of #if(_LOCAL_DIMMING_SUPPORT == _ON)

